Skip to content

NN build system: registry, dict batches, generic build path, transformer merge (steps 1-6)#390

Draft
max-dax wants to merge 9 commits into
hackathon-1from
nn-build-system
Draft

NN build system: registry, dict batches, generic build path, transformer merge (steps 1-6)#390
max-dax wants to merge 9 commits into
hackathon-1from
nn-build-system

Conversation

@max-dax

@max-dax max-dax commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Complete NN build-system refactor, tracking hackathon/NN_Build_System_Design.md — all six steps of §6. One commit per step (step 3 in three commits), each reviewable on its own. All 409 tests in tests/core + tests/gw pass.

What this PR does

Models are now declared as registry-typed components with flat kwargs, and every dimension is inferred from the data:

model:
  distribution:   {type: normalizing_flow, kwargs: {num_flow_steps: 30, ...}}
  embedding_net:  {type: dense_svd | transformer | pooling_transformer | <plugin>, kwargs: {...}}
  # context_merger: {type: concat | mlp}   # added automatically when the data has context_parameters
  • Registry + plugins — architectures resolve by short name → pip entry point → dotted path → file.py:Class; adding one no longer touches dingo source.
  • Dict batches — loaders yield named tensors; models take context: dict; tensor routing is declared per architecture (input_keys), not implied by loader ordering.
  • autocomplete_model_kwargs deleted — each architecture infers its own dims from a sample batch (complete_settings); completed settings are saved, so loading never needs data. Dims in user YAML are an error.
  • First-class conditioning — GNPE proxies and conditioning_parameters fill one declared context_parameters list; mergers (concat, mlp) or architecture-native handling. Chained-NPE training side (time_alignment) included.
  • Architecture-owned initializationinit_data_spec() / initialize_weights() hooks replace the trainer's hardcoded SVD stage; plugins get data-driven init for free.
  • Transformer merged (both stages: origin/transformer + the dingo-t1 augmentation/suppression machinery) as registered architectures. Transformer × FMPE and transformer + context parameters work with zero extra wiring — the payoff that the seam is real.
  • NN↔sampler interfaceinference_parameters / context_parameters / standardization accessors, enforced at save time (NN_Sampler_Interface.md).

Commits

Step Commit Summary
1 9fa54484 Characterization tests for the old build path
2 8dba80c3 Component registry; BasePosteriorModelNeuralDistribution (alias kept)
e17b5695 Parameter-contract accessors (interface part 1)
3a f917def2 Dict batches end to end
3b d54a1d33 New schema, registered embeddings, autocomplete_model_kwargs deleted
3c d2f9315a Chained-NPE training-side conditioning (mlp merger, time_alignment)
4 03838a12 Architecture-owned weight initialization; SVDBasis → core
5 08d9dbe9 Transformer as registered architecture; DenseResidualNet unified
6 87888a59 T1 augmentation transforms + inference-time token suppression

Detailed review notes: steps 1–3 · steps 4–6.

Compatibility

  • Old checkpoints load bit-identically: update_model_config is the single boundary mapping every old schema forward (incl. nsf+embedding); state-dict layouts are pinned by tests, including an old-format .pt round trip and a glasflow block-equivalence test.
  • Old-style YAML (posterior_model_type/posterior_kwargs/embedding_kwargs) keeps working through the same shim; examples, pipe defaults, and the docs notebook use the new schema.
  • Branches importing BasePosteriorModel keep working; the initial_weights= constructor argument is gone (heads-up for T1 rebases).

Coordination

  • Group A: accessor + dict-context signatures per NN_Sampler_Interface.md; we migrate factors.py once sampler-revamp reaches hackathon-1.
  • T1 authors: not ported from dingo-t1 (need their own decisions): pretraining, scheduler/AMP trainer changes, mixed ASD datasets, TimeShiftStrainGrid, BlockEncoding, skymaps.
  • Hydra structured configs are a follow-on PR (design §9; its two constraints are honored here).

🤖 Generated with Claude Code

@max-dax

max-dax commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Added the concrete metadata-contract proposal (the "(1)" item from the PR description): commit 4456a03 adds inference_parameters / context_parameters / standardization as properties on NeuralDistribution — the supported interface for samplers, working on old checkpoints unchanged.

Full proposal for the sampler group (incl. the step-3 dict-batch call-signature migration plan and the exact factors.py before/after): hackathon/NN_Sampler_Interface.md. Group A sign-off on that doc is the remaining coordination point.

max-dax and others added 3 commits July 12, 2026 17:43
Pin current behavior ahead of the NN build-system refactor (hackathon/
NN_Build_System_Design.md, step 1): build_model_from_kwargs dispatch,
autocomplete_model_kwargs dimensional completion (plain + GNPE),
end-to-end forward passes for all three posterior model types,
GNPE added_context embedding, unconditional flow, SVD initial-weight
seeding, old nsf+embedding schema mapping, and save/rebuild round trip.

Also documents (without fixing) a latent bug: get_theta_embedding_net
reads 'frequencies' from the wrong settings level, so any nonzero value
crashes the FMPE/score build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Step 2 of the NN build-system refactor (hackathon/NN_Build_System_Design.md):

- dingo/core/registry.py: generic Registry with four-step name resolution
  (registered name -> entry points ('dingo.architectures') -> dotted import
  path -> 'file.py:Class'), plus the NEURAL_DISTRIBUTIONS / EMBEDDING_NETS /
  CONTEXT_MERGERS instances. Third-party architectures no longer require
  editing dingo source.
- The three model types register themselves; build_model_from_kwargs
  dispatches via the registry instead of the hardcoded models_dict, so
  posterior_model_type can also name a plugin (tested end-to-end with a
  file-path plugin).
- Name lookup is now case-sensitive; the historical case-insensitivity for
  built-in type names moved into update_model_config (the back-compat
  boundary), so old checkpoints still load.
- BasePosteriorModel -> NeuralDistribution ('posterior' was too narrow: the
  same class models priors/proposals, e.g. the unconditional flow). Lasting
  alias BasePosteriorModel kept for existing imports and branches.

Behavior pinned by tests/core/test_build_model.py is unchanged (all pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
inference_parameters / context_parameters / standardization as properties:
the interface FlowFactor.from_model (sampler-revamp) consumes, so samplers
stop dict-spelunking metadata. Reads the model's own train_settings['data']
(matching core/samplers.py semantics, also for unconditional models); works
on old checkpoints unchanged. Proposal doc: hackathon/NN_Sampler_Interface.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
max-dax and others added 3 commits July 12, 2026 18:25
Batches are now dicts of named tensors end to end (NN_Build_System_Design
§4.4): SelectKeys replaces UnpackDict at the end of the training transform
chain, train/test epochs move a dict to device, and the NeuralDistribution
methods sample / sample_and_log_prob / log_prob / loss take a single context
dict (None for unconditional models) instead of positional *context tensors.

FlowWrapper and ContinuousFlow route context[k] for k in context_keys into
the embedding network, so tensor ordering is declared once at build time
instead of being implied by the loader's key order; a missing key raises a
ValueError naming it. Tensors stay positional inside the network modules.
autocomplete_model_kwargs reads the named sample, which removes the
GNPE-proxy detection via IndexError and the overloaded third data slot.
Sampler call sites and SampleDataset build the context dict directly.

The sampler-side dict signature is part 2 of the NN <-> sampler interface
(hackathon/NN_Sampler_Interface.md §2); the sampler-revamp call-site updates
follow once both branches are on hackathon-1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New model-settings schema (NN_Build_System_Design §4.3/4.5/4.7): a model
declares distribution / embedding_net / context_merger, each a registry type
plus a flat kwargs dict. Embedding networks follow a small contract
(input_keys, output_dim, complete_settings): dense_svd (the classic
LinearProjectionRB + DenseResidualNet stack) and the concat context merger
are the first registered components. NeuralDistribution.build_embedding_net
assembles them generically, so initialize_network loses its
embedding_kwargs-presence branches, and any embedding composes with any
distribution type.

complete_model_settings replaces autocomplete_model_kwargs: each architecture
infers its own input dims from a named sample batch, the generic theta/context
dims are computed once, and the completed settings are saved in the checkpoint
so loading never needs a data sample. Dimensions in user settings and unused
context mergers are errors. The V_rb_list deepcopy hack is gone: initial
weights are extra constructor kwargs and never touch the saved settings.
save_model now enforces that the standardization covers inference and context
parameters (NN_Sampler_Interface §1).

update_model_config remains the single back-compat boundary: it maps the old
posterior_model_type / posterior_kwargs / embedding_kwargs schema (and the
older nsf+embedding form) onto the new one, including added_context ->
concat merger; old checkpoints build state-dict-identical networks (pinned
by test). Deleted along the way: the never-wired embedding_net_builder
parameter, create_nsf_wrapped, create_nsf_with_rb_projection_embedding_net.
Example configs, pipe defaults, and the architecture docs notebook use the
new schema.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Folds the training side of the chained-NPE branch into the conditioning
design (NN_Build_System_Design §4.5): user-declared
data.conditioning_parameters feed the same context_parameters list as GNPE
proxies (two sources, one declared contract), the ContextMergerMLP lands as
the registered "mlp" context merger (mixes context through a learned MLP so
context_dim does not grow; dimensions inferred at completion instead of
hand-written input_dim), and data.time_alignment=True trains on
time-aligned data by skipping the per-detector time shift in
ProjectOntoDetectors, with a validation guard on the required conditioning.
Tests ported from the chained branch. Its inference side is a
factorized-sampler chain (Group A) and is not built here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@max-dax

max-dax commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator Author

Step 3 landed: dict batches, registry-built embeddings, autocomplete_model_kwargs deleted

Three new commits (f917def2, d54a1d33, d2f9315a) complete step 3 of the build-system design (§6). All 321 tests in tests/core + tests/gw pass.

Note on the rebase: the branch was rebased onto the updated hackathon-1. #387 had added a smaller tests/core/test_build_model.py; the conflict was resolved in favor of the more comprehensive version from this branch. One #387 test asserting case-insensitive dispatch was dropped deliberately: per the step-2 decision, registry lookups are case-sensitive, and historical case-tolerance lives only in update_model_config.

1. f917def2 — dict batches (design §4.4, interface doc §2)

  • The training transform chain ends in SelectKeys (dict-preserving) instead of UnpackDict (positional flattening); loaders yield {"inference_parameters": ..., "waveform": ..., "context_parameters": ...}.
  • NeuralDistribution.sample / sample_and_log_prob / log_prob / loss take a single context: dict | None instead of positional *context — exactly the signature proposed to Group A in NN_Sampler_Interface.md §2.
  • FlowWrapper and ContinuousFlow route context[k] for k in context_keys into the embedding network once, at the model boundary; tensors stay positional inside modules. A missing context key raises a ValueError naming it — no more silent dependence on the loader's key order.
  • This kills the GNPE-proxy detection via try/except IndexError and the overloaded data_sample[2] slot (which the transformer branch reused for position tensors — the two were mutually exclusive by accident).
  • Call sites updated: core/samplers.py (plain + GNPE), SampleDataset for unconditional training.

2. d54a1d33 — new schema + registries; autocomplete_model_kwargs deleted (design §4.3/4.5/4.7/4.8)

New model-settings schema, each component a registry type plus a flat kwargs dict:

model:
  distribution:    {type: normalizing_flow, kwargs: {num_flow_steps: 30, ...}}
  embedding_net:   {type: dense_svd, kwargs: {output_dim: 128, ...}}
  # context_merger: {type: concat}   # added automatically when the data has context_parameters
  • Embedding contract (core/nn/enets.py module docstring): input_keys, output_dim, complete_settings(settings, sample_batch). First registered components: dense_svd (LinearProjectionRB + DenseResidualNet) and the concat context merger. Any embedding now composes with any distribution type — transformer×FMPE will need zero extra wiring.
  • complete_model_settings replaces autocomplete_model_kwargs: each architecture infers its own input dims from the named sample batch; generic theta_dim/context_dim are computed once; completed settings are saved in the checkpoint so loading never needs a data sample. Dimensions in user YAML are an error; a context_merger with no context parameters in the data is an error (no silent drops).
  • initialize_network loses its embedding_kwargs-presence branches; NeuralDistribution.build_embedding_net assembles embedding + merger generically. The V_rb_list deepcopy hack is gone — initial weights are extra constructor kwargs, never in saved settings.
  • save_model enforces the parameter contract promised to samplers (interface doc §1): standardization must cover inference ∪ context parameters, for built-ins and plugins alike.
  • Back-compat: update_model_config is the single boundary and maps all old schemas forward (nsf+embedding, posterior_model_type/posterior_kwargs/embedding_kwargs, added_context → concat merger, input_dimtheta_dim). Old checkpoints build state-dict-identical networks — pinned by test_old_schema_builds_state_dict_compatible_network and an actual old-format .pt load round trip (test_load_old_schema_checkpoint_file).
  • Deleted: the never-wired embedding_net_builder parameter, create_nsf_wrapped, create_nsf_with_rb_projection_embedding_net. Example configs, pipe defaults, and the architecture docs notebook use the new schema (old YAML still works through the shim).

3. d2f9315a — chained-NPE training-side conditioning (design §4.5)

  • data.conditioning_parameters feed the same context_parameters list as GNPE proxies — two sources, one declared contract.
  • ContextMergerMLP ported as the registered mlp merger: context mixed in through a learned MLP so context_dim doesn't grow; its dims are inferred at completion (no hand-written input_dim), output_dim defaults to the embedding's.
  • data.time_alignment: true trains on time-aligned data via ProjectOntoDetectors(apply_time_shift=False), with the validation guard (requires ra/dec/geocent_time as conditioning, forbids them as inference targets, incompatible with gnpe_time_shifts). Tests ported from the chained branch. The chained inference side is a factorized-sampler chain (Group A's domain) and is not built here.

Deliberately deferred

  • FlowFactor/GNPEFlowFactor call sites (interface doc §2, impacts 1–3): factors.py is not in hackathon-1 yet, so those edits can't land in this PR. Nothing on sampler-revamp breaks in the meantime; we'll do that migration as promised once the branches meet.
  • Step 4 (architecture-owned SVD init — the initial_weights threading and the trainer's svd gate still exist, marked with a TODO) and steps 5–6 (transformer merge) follow.

Review pointers

  • The schema mapping: core/utils/backward_compatibility.py::update_model_config
  • The autocomplete replacement: core/posterior_models/build_model.py::complete_model_settings
  • The embedding contract + registered components: core/nn/enets.py
  • Checkpoint compatibility tests: tests/core/test_build_model.py (old-schema section)

🤖 Generated with Claude Code

max-dax and others added 3 commits July 12, 2026 20:22
Data-driven weight initialization moves behind two hooks on the embedding
contract (NN_Build_System_Design §4.6): init_data_spec() declares the data
variation a network wants (noise on/off, network formatting, pinned prior
parameters, sample count), and initialize_weights(batches, out_dir) consumes
a matching batch iterator. DenseSVDEmbedding implements them, absorbing the
SVD math from build_svd_for_embedding_network: it collects per-block strains,
builds one SVD basis per block, saves validation diagnostics, drops the
zero rows outside its input range, and seeds the projection layer.

The trainer no longer knows about the SVD: prepare_training_new walks
pm.network.modules(), answers any module's spec via the new
initialization_dataloader (a transform-stack variation using the existing
omit_transforms mechanism), and lets the module initialize itself — plugin
architectures get data-driven initialization without touching dingo.
Seeding triggers on svd.num_training_samples, so loading a saved model never
re-runs it. Deleted: build_svd_for_embedding_network, the initial_weights
threading through NeuralDistribution/build_model_from_kwargs, and the
"embedding network is assumed to have an SVD projection layer" special case.

SVDBasis moves from dingo/gw/SVD.py to dingo/core/SVD.py — it was always
domain-agnostic (numpy/scipy only) and core may not import from gw; the old
module remains as a re-export shim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the transformer branch (origin/transformer, stage 1 of the T1 merge)
onto the registry build system. TransformerEmbedding registers as
"transformer": tokenized strain -> Tokenizer (shared DenseResidualNet with
GLU position-conditioning) -> TransformerEncoder -> CLS/average pooling ->
optional final net. complete_settings infers the tokenizer input dims and
num_blocks from a named sample batch (position is its own batch entry now,
instead of overloading the GNPE-proxy slot). Because the seam is generic,
transformer x flow-matching works with zero extra wiring (pinned by test),
and context parameters compose via the concat merger - both impossible on
the original branch. StrainTokenization lands as a data transform, wired
into set_train_transforms and GWSampler via the tokenization data settings;
core samplers accept dict outputs from transform_pre.

DenseResidualNet is unified rather than duplicated: the branch's
implementation (per-block GLU context, optional layer_norm, arbitrary
leading batch dims) moves to core/nn/resnet.py and replaces the
glasflow-block-based version in enets.py - MyResidualBlock is a strict
superset of glasflow's ResidualBlock with identical parameter names and
forward pass (pinned by test), so old checkpoints load unchanged. Only the
rq-coupling conditioner is genuinely a different architecture; per the
team decision it is selected by an explicit conditioner_type setting
(glasflow_residual default | dense_residual with optional layer_norm), not
a layer_norm flag. layer_norm threads through the cfnets builders, and
ContinuousFlow no longer shares a mutable-default Identity module.

Not ported (deliberate): the advertised-but-dead augmentation config keys
(drop_detectors, drop_frequency_range - stage 2, from dingo-t1), the
allow_tf32 pop-and-discard, the embedding_type branches (registry handles
dispatch), and create_nsf_with_transformer_embedding_net. The
ConcatContextMerger now routes multi-input embeddings. Registration of
built-in architectures moves to dingo/core/nn/__init__.py. Adds
examples/transformer_model and the branch's transformer / resnet /
tokenization / sampler-transform tests, adapted to the registry API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage 2 of the transformer merge, from origin/dingo-t1 (build-system step 6).

Training-side augmentation for tokenized models, wired via data.tokenization:
DropDetectors, DropFrequenciesToUpdateRange (f_cut), DropFrequencyInterval
(mask_interval), DropRandomTokens, and NormalizePosition, all marking tokens
in drop_token_mask that the transformer then does not attend to. Ported
near-verbatim with two deviations: DropDetectors gains unbatched-sample
support (our pipeline feeds single samples; the other drop transforms already
handled this - covered by the ported no-batch test), and DropRandomTokens
loses the increase_p_until_epoch ramp, which is dead on dingo-t1 as well
(the epoch injection into samples is commented out there).

Inference side: GWSampler gains a suppress property ([f_lo, f_hi] or
per-detector dict) with real validation (dingo-t1 left it as an unimplemented
warning stub): it requires a tokenized model trained with drop augmentation
and an in-domain interval. Frequency-range updates and suppression for
tokenized models go through the ported UpdateFrequencyRange transform (masks
tokens instead of zeroing strain data), with NormalizePosition applied after
mask updates, matching training. Unchanged frequency bounds are passed as
None so the domain default does not needlessly mask the zero-padded final
token. UpdateFrequencyRange gets the test dingo-t1 left as a TODO.

PoolingTransformer (sinusoidal MultiPositionalEncoding + average pooling)
registers as the "pooling_transformer" embedding, composing with any
distribution through the generic build path. The transformer example config
now demonstrates the augmentation settings. Not ported (out of step-6 scope,
separate research features on dingo-t1): pretraining
(enets_pretraining/pretraining_model), custom schedulers/AMP trainer changes,
mixed ASD datasets, TimeShiftStrainGrid, BlockEncoding, skymaps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@max-dax

max-dax commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator Author

Steps 4–6 landed: init hook, transformer merge, T1 augmentation — the plan is complete

Three further commits (03838a12, 08d9dbe9, 87888a59) finish the build-system design §6 sequence. All 409 tests in tests/core + tests/gw pass. The PR now carries the full refactor; implementation deltas from the design sketches are footnoted in the design doc (§4.3, §4.6).

4. 03838a12 — architecture-owned initialization (design §4.6)

  • Two optional hooks on the embedding contract: init_data_spec() declares the data variation a network wants for weight initialization (spec keys: noise, network_format, fix_parameters, num_samples), and initialize_weights(batches, out_dir) consumes a matching batch iterator. DenseSVDEmbedding implements them, absorbing all of build_svd_for_embedding_network (incl. validation diagnostics; V-matrix truncation now uses the network's own num_bins, so core stays domain-agnostic).
  • The trainer is generic: prepare_training_new walks pm.network.modules() and answers any module's spec via initialization_dataloader (train_builders), which maps the spec onto the existing omit_transforms mechanism. Hooks compose through wrappers (mergers, FlowWrapper) and plugins get data-driven init without touching dingo. Seeding triggers on svd.num_training_samples, so loading a saved model never re-runs it.
  • Deleted: build_svd_for_embedding_network, the entire initial_weights constructor threading, and the "embedding network is assumed to have an SVD projection layer" trainer special case. Note for the transformer/T1 authors: the initial_weights= constructor argument no longer exists.
  • SVDBasis moved dingo/gw/SVD.pydingo/core/SVD.py (it was always domain-agnostic; the old path re-exports).

5. 08d9dbe9 — transformer merge, stage 1 (from origin/transformer)

  • TransformerEmbedding registers as transformer: tokenized strain → position-conditioned TokenizerTransformerEncoder → CLS/average pooling → optional final net. complete_settings infers tokenizer dims and num_blocks from the named sample batch — position is a regular batch entry, no longer squatting in the GNPE-proxy slot.
  • The seam paid off, pinned by tests: transformer × flow-matching works with zero extra wiring, and transformer + context_parameters composes via the concat merger — both impossible on the original branch.
  • DenseResidualNet unified, not duplicated: the branch's block (MyResidualBlock) is a strict superset of glasflow's ResidualBlock — identical parameter names and forward pass at layer_norm=False (pinned by an equivalence test) — so it moved to core/nn/resnet.py and replaced the glasflow-block-based version everywhere. Old checkpoints load unchanged. The one genuinely incompatible case, the rq-coupling conditioner, is selected by an explicit base_transform_kwargs.conditioner_type: glasflow_residual | dense_residual (per the team decision: a distinct type, not a layer_norm flag).
  • StrainTokenization wired via data.tokenization in training and GWSampler inference. Not ported: the dead drop_* config keys (→ step 6), the allow_tf32 pop, the embedding_type branches. New examples/transformer_model config.

6. 87888a59 — T1 augmentation + token suppression (from origin/dingo-t1)

  • Training augmentation via data.tokenization: DropDetectors, DropFrequenciesToUpdateRange (f_cut), DropFrequencyInterval (mask_interval), DropRandomTokens, NormalizePosition. Two deviations from dingo-t1, both deliberate: DropDetectors gained unbatched-sample support (our pipeline feeds single samples), and DropRandomTokens lost the increase_p_until_epoch ramp — dead on dingo-t1 too (the epoch injection is commented out there).
  • Inference: GWSampler.suppress ([f_lo, f_hi] or per-detector dict) with real validation replacing T1's warning stub; tokenized models route frequency updates through the ported UpdateFrequencyRange (token masks instead of zeroed strain). Two T1 quirks fixed: positions are normalized after Hz-threshold comparisons, and unchanged domain bounds are passed as None so the zero-padded final token isn't spuriously masked. UpdateFrequencyRange got the test T1 left as a TODO.
  • PoolingTransformer (MultiPositionalEncoding + average pooling) registers as pooling_transformer — a third tokenized architecture through the same generic path.
  • Deliberately not ported (separate research features on dingo-t1, need their own decisions): pretraining (enets_pretraining/pretraining_model), scheduler/AMP trainer changes, mixed ASD datasets, TimeShiftStrainGrid, BlockEncoding, skymaps.

Review pointers

  • Init hooks + trainer glue: core/nn/enets.py (init_data_spec/initialize_weights), gw/training/train_pipeline.py::prepare_training_new, gw/training/train_builders.py::initialization_dataloader
  • Block equivalence (checkpoint compatibility): tests/core/test_resnet.py::test_residual_block_equivalent_to_glasflow
  • Transformer contract + cross-distribution tests: core/nn/transformer.py, tests/core/test_build_model.py (transformer section)
  • Augmentation transforms + ported tests: gw/transforms/tokenization_transforms.py, tests/gw/transforms/test_tokenization_augmentation.py
  • Token suppression: gw/inference/gw_samplers.py (suppress, _initialize_transforms), tests/gw/inference/test_gw_sampler_transforms.py

Open follow-ups (outside this PR)

  • factors.py migration to the accessors + dict context once sampler-revamp reaches hackathon-1 (interface doc §2 — we do those edits).
  • Hydra structured configs as their own PR (design §9; both constraints honored here).
  • T1 leftovers above, to be scoped with the T1 authors.

🤖 Generated with Claude Code

@max-dax max-dax changed the title NN build system: characterization tests + component registry (steps 1-2) NN build system: registry, dict batches, generic build path, transformer merge (steps 1-6) Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant